import asyncio
import requests
import time
import aiohttp

"""
因为我们请求网页之后需要等待页面响应并返回结果。耗时等待操作一般都是 IO 操作，比如文件读取、网络请求等。
协程对于处理这种操作是有很大优势的，当遇到需要等待的情况时，程序可以暂时挂起，转而去执行其他操作，从而避免一直等待一个程序而耗费过多的时间，充分利用资源。

为了表现出协程的优势，我们还是以本节开头介绍的网站 https://httpbin.org/delay/5 为例，因为该网站响应比较慢，所以我们可以通过爬取时间来直观感受到爬取速度的提升。
"""


"""
 示例2 协程实现
 解一下 await 的用法，它可以将耗时等待的操作挂起，让出控制权。当协程执行的时候遇到 await，时间循环就会将本协程挂起，转而去执行别的协程，直到其他协程挂起或执行完毕
 
 await 后面的对象必须是如下格式之一（具体可以参见 https://www.python.org/dev/peps/pep-0492/#await-expression）：

一个原生 coroutine 对象；
一个由 types.coroutine 修饰的生成器，这个生成器可以返回 coroutine 对象；
一个包含 __await__ 方法的对象返回的一个迭代器。
"""
# start = time.time()
#
#
# async def get(url):
#     return requests.get(url)
#
#
# async def request():
#     url = 'https://httpbin.org/delay/5'
#     print('Waiting for', url)
#     # 挂起
#     response = await get(url)
#     print('Get response from', url, 'response', response)
#
# tasks = [asyncio.ensure_future(request()) for _ in range(5)]
# loop = asyncio.get_event_loop()
# loop.run_until_complete(asyncio.wait(tasks))
#
# end = time.time()
# print('Cost time:', end - start)


"""
  上面它还不是异步执行的，也就是说我们仅仅将涉及 IO 操作的代码封装到 async 修饰的方法里面是不可行的。我们必须要使用支持异步操作的请求方式才可以实现真正的异步，所以这里就需要 aiohttp 派上用场了。
  本质上就是request 会堵塞
"""


"""
  示例3 使用aiohttp+协程 真正实现异步
  aiohttp 是一个支持异步请求的库，配合使用它和 asyncio，我们可以非常方便地实现异步请求操作
  pip install aiohttp
  
  
  开始运行时，时间循环会运行第一个 task。针对第一个 task 来说，当执行到第一个 await 跟着的 get 方法时，它被挂起，但这个 get 方法第一步的执行是非阻塞的，挂起之后立马被唤醒，所以立即又进入执行，
  创建了 ClientSession 对象，接着遇到了第二个 await，调用了 session.get 请求方法，然后就被挂起了。
  由于请求需要耗时很久，所以一直没有被唤醒，好在第一个 task 被挂起了，那么接下来该怎么办呢？事件循环会寻找当前未被挂起的协程继续执行，于是就转而执行第二个 task 了，也是一样的流程操作，直到执行了第十个 task 的 session.get 方法之后，全部的 task 都被挂起了。所有 task 都已经处于挂起状态，那咋办？只好等待了。5 秒之后，几个请求几乎同时都有了响应，然后几个 task 也被唤醒接着执行，输出请求结果，最后总耗时 6 秒！
  

   怎么样？这就是异步操作的便捷之处，当遇到阻塞式操作时，任务被挂起，程序接着去执行其他任务，而不是傻傻地等着，这样可以充分利用 CPU 时间，而不必把时间浪费在等待 IO 上。
"""

start = time.time()


async def get(url):
    session = aiohttp.ClientSession()
    response = await session.get(url)
    await response.text() # 确保真正发起了请求并把数据“读全”。
    await session.close() # 及时释放网络资源，避免连接泄漏。
    return response


async def request():
    url = 'https://httpbin.org/delay/5'
    print('Waiting for', url)
    # 挂起
    response = await get(url)
    print('Get response from', url, 'response', response)

tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

end = time.time()
print('Cost time:', end - start)




